#include "vcl_stl.h"

#include "bkbase.h"
#include "h_base.h"
#include "h_xml.h"
using namespace h_base;

#include "simbat.h"
using namespace simbat;

#include "bkipc.h"

struct HClusterNodeInfo
{
   int Id;
   HString Addr;
   int Port;

   HClusterNodeInfo()
      : Id(-1), Port(-1)
   {}
};

const int COMM_TIME_OUT = 30000;

static const char *SimbatBase = NULL;
static char ErrMsg[8192 + 1];

static HString SyslogDir;
static HSyslog Syslog;
static int LocalPort;
static HString ShrDir;

static bool ClusterFlag = false;                 // 是否集群
static HNodeRole MyRole = nrMaster;              // 本服务器角色（MASTER/SLAVE/BACKUP）
static int MyId = -1;                            // 本服务器ID（从0开始）
static vector<HClusterNodeInfo> ClusterNodeVec;  // 集群配置信息

static void LoadFromCfg(HXmlTree &Cfg)
{
   SyslogDir = EnvEvaluate(Cfg.GetValue("SYSLOG_DIR", "$SIMBAT_BASE/etc"));
   Syslog.Setup(SyslogDir, Cfg.GetValueUpper("SYSLOG_LEVEL", "INFO"));

   if ( !GetMyId(SimbatBase, MyId, MyRole, ErrMsg) )
   {
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      return;
   }

   LocalPort = StrToInt(Cfg.GetValue("PORT").c_str());
   ShrDir = EnvEvaluate(Cfg.GetValue("SHR_DIR", "$SIMBAT_BASE/shr"));

   {
      vector<HString> StrVec;
      Cfg.GetValues("CLUSTER", StrVec);
      ClusterFlag = StrVec.size() > 1;
      if ( ClusterFlag )
      {
         ClusterNodeVec.clear();
         for ( int i = 0; i < StrVec.size(); i++ )
         {
            HString &AddrStr = StrVec[i];
            vector<HString> AddrPort;
            SplitString(AddrStr.c_str(), ':', AddrPort, true);
            if ( AddrPort.size() != 2 && AddrPort.size() != 3 )
            {
               snprintf(ErrMsg, 8192, "Invalid cluster address(%s)", AddrStr.c_str());
               Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
               return;
            }
            HClusterNodeInfo Node;
            Node.Id = i;
            Node.Addr = AddrPort[0];
            Node.Port = StrToInt(AddrPort[1].c_str());
            if ( Node.Port <= 2048 )
            {
               snprintf(ErrMsg, 8192, "Port %s too small, must be greater than 2048", AddrStr.c_str());
               Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
               return;
            }
            ClusterNodeVec.push_back(Node);
         }
      }
      else
      {
         HClusterNodeInfo Node;
         Node.Id = 0;
         Node.Addr = "127.0.0.1";
         Node.Port = LocalPort;
         ClusterNodeVec.push_back(Node);
      }
   }
}

static bool LoadCfg(HString &ErrStr)
{
   char FileName[500 + 1];
   HXmlTree Cfg;

   {
      HFileLockRead Lock("$SIMBAT_BASE/tmp/config.lck");
      snprintf(FileName, 500, "%s/etc/config.xml", SimbatBase);
      if ( !Cfg.LoadFromFile(FileName) )
      {
         ErrStr = Cfg.GetErrorString();
         return false;
      }
   }

   LoadFromCfg(Cfg);
   return true;
}

static int SetVar(const char *Addr, const char *Name, const char *Value)
{
   snprintf(ErrMsg, 8192, "SetVar(Addr = %s, VarName = %s, Value = %s)", Addr, Name, Value);
   Syslog.Log(__FILE__, __LINE__, llInfo, "ENTRY", ErrMsg);

   vector<HString> AddrVec;

   SplitString(Addr, ',', AddrVec, true);

   for ( int i = 0; i < AddrVec.size(); i++ )
   {
      HString &IpPort = AddrVec[i];
      if ( strchr(IpPort.c_str(), ':') == NULL )
      {
         char TmpStr[500 + 1];
         snprintf(TmpStr, 500, "%s:%d", IpPort.c_str(), LocalPort);
         IpPort = TmpStr;
      }
   }

   HString Result;

   for ( int i = 0; i < AddrVec.size(); i++ )
   {
      HString &IpPort = AddrVec[i];
      char Buf[500 + 1];
      snprintf(Buf, 500, "g|||%s|%s", Name, Value);
      if ( CallServer(Syslog, COMM_TIME_OUT, IpPort, "SET_VAR", "", Buf, true, Result) )
      {
         return 0;
      }
   }

   BkPrintError("CallServer fail, result = %s", Result.c_str());
   return 8;
}

void PrintUsage()
{
   fprintf(stderr, "Usage : [PATH]/simbat_setvar [--addr=host_name:port[,host_name2:port2]] id value\n");
}

int main(int argc, char *argv[])
{
   if ( strchr(argv[0], '/') != NULL )
   {
      SetBaseByPath(argv[0]);
   }

   if ( argc == 1 )
   {
      PrintUsage();
      return 8;
   }

   SimbatBase = getenv("SIMBAT_BASE");

   if ( SimbatBase == NULL )
   {
      fprintf(stderr, "$SIMBAT_BASE not defined\n");
      return 8;
   }

   HString ErrStr;

   if ( !LoadCfg(ErrStr) )
   {
      BkPrintError("Load config.xml fail, %s", ErrStr.c_str());
      PrintUsage();
      return 8;
   }

   int i = 1;
   const char *Addr = "127.0.0.1";

   for ( ; i < argc; i++ )
   {
      if ( memcmp(argv[i], "--addr=", 7) == 0 )
      {
         Addr = argv[i] + 7;
         continue;
      }
      if ( argv[i][0] == '-' )
      {
         PrintUsage();
         return 8;
      }
      else
      {
         break;
      }
   }

   if ( argc != i + 2 )
   {
      PrintUsage();
      return 8;
   }

   return SetVar(Addr, argv[i], argv[i + 1]);
}
